package com.ushine.web.communication.netty.handler;


import com.ushine.web.communication.netty.NettySocketServer;
import com.ushine.web.communication.netty.proto.ErrorMessageOuterClass;
import com.ushine.web.communication.netty.proto.Message;
import com.ushine.web.communication.netty.service.INettyService;
import com.ushine.web.communication.threads.CommunicationThreads;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.Attribute;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: yls
 * @Date 2022/5/11 10:17
 * @Description: 注册处理器
 * @Version 1.0
 **/
@Slf4j
public class RegisterHandler extends ChannelInboundHandlerAdapter {

    /**
     * 通道存储容器
     */
    public static final Map<String, Channel> CHANNEL_MAP = new ConcurrentHashMap<>();
    /**
     * 通道存储集合
     */
    private static final ChannelGroup CHANNELS = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * 通信服务
     */
    private final INettyService nettyService;

    public RegisterHandler(INettyService nettyService) {
        this.nettyService = nettyService;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        Message.Instruction message = (Message.Instruction) msg;
        if (message.getType() == Message.Instruction.Type.REGISTER) {
            registerDevice(ctx.channel(), message.getSerialNumber());
        } else {
            if (checkRegister(ctx.channel(), message.getSerialNumber())) {
                super.channelRead(ctx, msg);
            } else {
                log.warn("设备未注册,序列号:" + message.getSerialNumber());
            }

        }
    }

    /**
     * 功能描述:
     * 〈 注册设备 〉
     *
     * @param channel      通道
     * @param serialNumber 主机序列号
     * @return : void
     * @author : yls
     * @date : 2022/5/11 10:46
     */
    private void registerDevice(Channel channel, String serialNumber) {
        InetSocketAddress inetSocketAddress = (InetSocketAddress) channel.remoteAddress();
        log.info("Registering device:{}", inetSocketAddress.getAddress().getHostAddress());
        if (nettyService.checkSerialNumber(inetSocketAddress.getAddress().getHostAddress(), serialNumber)) {
            Attribute<String> attr = channel.attr(NettySocketServer.ATTR_KEY);
            attr.set(serialNumber);
            //存储通道
            CHANNELS.add(channel);
            CHANNEL_MAP.put(serialNumber, channel);
            Runnable runnable = () -> nettyService.regist(serialNumber);
            CommunicationThreads.EXECUTOR.execute(runnable);
            log.info("设备注册成功,序列号:" + serialNumber);
            nettyService.sendVersion(serialNumber);
        } else {
            Runnable runnable = () -> nettyService.logOut(serialNumber);
            CommunicationThreads.EXECUTOR.execute(runnable);
            log.info("设备注册失败,序列号:" + serialNumber);
        }
    }

    /**
     * 功能描述:
     * 〈检查是否注册〉
     *
     * @param channel      通道实例
     * @param serialNumber 序列号
     * @return : boolean true:已注册,false:未注册
     * @author : yls
     * @date : 2022/5/11 10:47
     */
    private boolean checkRegister(Channel channel, String serialNumber) {
        if (!CHANNEL_MAP.containsKey(serialNumber)) {
            Message.Instruction instruction = Message.Instruction.newBuilder().setType(Message.Instruction.Type.ERROR)
                    .setError(ErrorMessageOuterClass.ErrorMessage.newBuilder().setType(ErrorMessageOuterClass.ErrorMessage.Type.UNREGISTERED).setMessage("设备未注册!").build())
                    .build();
            channel.writeAndFlush(instruction);
            return false;
        } else {
            return true;
        }
    }

    /**
     * 功能描述:
     * 〈离线触发〉
     *
     * @param ctx 通道信息
     * @return : void
     * @author : yls
     * @date : 2022/5/11 10:47
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        Attribute<String> attribute = ctx.channel().attr(NettySocketServer.ATTR_KEY);
        if (!StringUtils.isEmpty(attribute.get())) {
            CHANNEL_MAP.remove(attribute.get());
            nettyService.logOut(attribute.get());
            log.warn("设备下线,设备序列号:" + attribute.get());
        }
    }
}
